#include <jni.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/dirent.h>
#include <android/log.h>

#include "com_nakaikensuke_SNE_EntriesIterator.h"

#define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, struct dirent [2])
#define VFAT_IOCTL_READDIR_SHORT  _IOR('r', 2, struct dirent [2])

// #define __DEBUG__

static int _fd = 0;							// ファイルポインタ
static struct dirent _entries[2] = {0};	// ディレクトリエントリ用バッファ１

/*
 *	グローバル変数を初期化する
 */
static void initialize()
{
	int i, j;
 	for(i = 0; i < 2; i ++) {
		_entries[0].d_ino = 0;
		_entries[0].d_off = 0;
		_entries[0].d_reclen = 0;
		for(j = 0; j < 256; j ++)
			_entries[0].d_name[j] = '\0';
	}
}

/*
 * Class:     com_example_shortfilename_EntriesIterator
 * Method:    open
 * Signature: (Ljava/lang/String;)Z
 */
jboolean
Java_com_nakaikensuke_SNE_EntriesIterator_open(JNIEnv* env, jobject me, jstring param)
{
#ifdef __DEBUG__
  __android_log_print(ANDROID_LOG_DEBUG, "SHORTNAME", "open invoked");
#endif
  
  	///	ファイルをオープンする
  	const char* name = (*env)->GetStringUTFChars(env, param, NULL);
	_fd = open(name, O_RDONLY);
	if(_fd == 0)
		return JNI_FALSE;
		
	(*env)->ReleaseStringUTFChars(env, param, name);

	initialize();
	return JNI_TRUE;
}

/*
 *	バイト配列を初期化する
 */
void setupCharArray(JNIEnv* env, jbyteArray array, char* name, int length)
{
#ifdef __DEBUG__
  __android_log_print(ANDROID_LOG_DEBUG, "SHORTNAME", "setupCharArray invoked1");
#endif

  jbyte* elements = (*env)->GetByteArrayElements(env, array, NULL);
  int i;
  for(i = 0; i < length; i ++)
    elements[i] = name[i];
  
  (*env)->ReleaseByteArrayElements(env, array, elements, 0);
}

/*
 *	ディレクトリか否か調査する
 */
jboolean isDirectory(JNIEnv* env, jobject me, char* name, int length)
{
	///	呼び出し元の「_path」フィールドからチェック中のディレクトリ名を取得する
	jclass clazz = (*env)->GetObjectClass(env, me);
	jfieldID field = (*env)->GetFieldID(env, clazz, "_path", "Ljava/lang/String;");
	jstring path = (*env)->GetObjectField(env, me, field);
	
	///	_pathフィールド（String※UNICODE）をUTF8に変換する
	const char* parent = (*env)->GetStringUTFChars(env, path, 0);
	int parentSize = (*env)->GetStringUTFLength(env, path);
	
	///	検査するエントリのパスを作成する
	int size = _entries[1].d_reclen + 1 + parentSize + 1;
	char* childPath = (char*)malloc(size);
	if(childPath == NULL) {
		(*env)->ReleaseStringUTFChars(env, path, parent);
		return JNI_FALSE;
	}

	memset(childPath, 0, size);
	memcpy(childPath, parent, parentSize);
	childPath[parentSize] = '/';
	memcpy(childPath + parentSize + 1, _entries[1].d_name, _entries[1].d_reclen);

	/// 作成したエントリをopenする
	int fd = open(childPath, O_RDONLY);

	free(childPath);
	(*env)->ReleaseStringUTFChars(env, path, parent);

	if(fd == 0)
		return JNI_FALSE;

	///	fstatでこのエントリがディレクトリなのかファイルなのかを確認する
	jboolean isDir = JNI_FALSE;
 	struct stat status;
	int ret = fstat(fd, &status);
	if(ret == 0) {
		if(S_ISDIR(status.st_mode))
			isDir = JNI_TRUE;
	}
	
	close(fd);
	return isDir;
}

/*
 * Class:     com_example_shortfilename_EntriesIterator
 * Method:    readdir
 * Signature: ()Lcom/example/shortfilename/DirEntry;
 */
jobject
Java_com_nakaikensuke_SNE_EntriesIterator_readdir(JNIEnv* env, jobject me, jstring encode)
{
#if __DEBUG__
  __android_log_print(ANDROID_LOG_DEBUG, "SHORTNAME", "readdir invoked");
#endif
 
 	///	ioctlを叩いてエントリ情報(ショートネームとロングネーム)を取得する
	int ret = ioctl(_fd, VFAT_IOCTL_READDIR_BOTH, &_entries);
 	if(ret == -1)
		return NULL;

	///	エントリ情報がなくなったらそこでおしまい（最後までエントリを読みきった）
	if(_entries[0].d_reclen == 0)
 		return NULL;

	///	取得したエントリがディレクトリなのかファイルなのか確認する
	jboolean isDir = isDirectory(
							env, me, _entries[1].d_name, _entries[1].d_reclen);

	///	以降で本メソッドが返すDirEntry型インスタンスを生成する
	jclass clazz = (*env)->FindClass(env, "com/nakaikensuke/SNE/DirEntry");
	jmethodID methoz = (*env)->GetMethodID(env, clazz, "<init>", "([B[BZLjava/lang/String;)V");

	///	DirEntryのコンストラクタに必要な情報を生成する
	jbyteArray param1 = (*env)->NewByteArray(env, _entries[0].d_reclen);
	setupCharArray(env, param1, _entries[0].d_name, _entries[0].d_reclen);

	jbyteArray param2 = (*env)->NewByteArray(env, _entries[1].d_reclen);
	setupCharArray(env, param2, _entries[1].d_name, _entries[1].d_reclen);

	///	DirEntry型インスタンスを生成する
	return (*env)->NewObject(env, clazz, methoz, param1, param2, isDir, encode);
}

/*
 * Class:     com_example_shortfilename_EntriesIterator
 * Method:    close
 * Signature: ()V
 */
void
Java_com_nakaikensuke_SNE_EntriesIterator_close(JNIEnv* env, jobject me)
{
#if __DEBUG__
  __android_log_print(ANDROID_LOG_DEBUG, "SHORTNAME", "close invoked");
#endif

	if(_fd != 0) {
		initialize();
		close(_fd);
		_fd = 0;
  }
}
